import { WatchButton } from "@/components/intelligence/watch-button"; import type { EventType, ProjectDetail } from "@dci/core"; import { partialDateSortKey } from "@dci/core"; import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import type { ReactNode } from "react"; import { Breadcrumbs } from "@/components/entity/breadcrumbs"; import { EntityMap } from "@/components/entity/entity-map"; import { JsonLd } from "@/components/entity/json-ld"; import { ExtLink, KeyValueList } from "@/components/entity/kv"; import { ProvenanceTable, WithProvenance } from "@/components/entity/provenance"; import { SectionNav } from "@/components/entity/section-nav"; import { SourcesFooter } from "@/components/entity/sources-footer"; import { EventList } from "@/components/event-list"; import { ClaimsTable, DataQualityPanel, Evidence, EvidenceProvider, NearbyInfrastructurePanel } from "@/components/evidence"; import { ProjectStages, ProjectVelocity } from "@/components/project-stages"; import { AiMark, Badge, ConfidenceBadge, EstimateMark, PrecisionBadge, ScopeBadge, SemanticsLabel, StatusBadge } from "@/components/ui/badge"; import { Page, Section } from "@/components/ui/card"; import { CountryFlag } from "@/components/ui/country-flag"; import { ErrorState, OfflineNotice } from "@/components/ui/states"; import { SourceHistory, TimelineList, type TimelineEntry } from "@/components/ui/timeline"; import { countryName, fmtDate, fmtInt, fmtMw, fmtUsd, placeLine, splitMw } from "@/lib/format"; import { fmtPartialDateSafe } from "@/lib/format-project"; import { CONFIDENCE_LABEL, EVENT_LABEL, PRECISION_LABEL, STATUS_LABEL, isSiteScopeLabel } from "@/lib/labels"; import { routes } from "@/lib/routes"; import { absoluteUrl, compactJsonLd, NOT_FOUND_METADATA, pageMetadata } from "@/lib/seo"; import { getProject } from "./data"; export const revalidate = 300; type Props = { params: Promise<{ slug: string }> }; function describe(p: ProjectDetail): string { if (p.description) return p.description; const bits = [`${p.name}: ${STATUS_LABEL[p.status].toLowerCase()} data center project`]; if (p.operator) bits.push(`by ${p.operator.name}`); const place = placeLine(p); if (place !== "—") bits.push(`in ${place}`); if (p.plannedMw !== null && siteScoped(p.capacityScope)) bits.push(`— ≈ ${fmtMw(p.plannedMw)} planned (published figure)`); if (p.investmentUsd !== null && siteScoped(p.investmentScope)) bits.push(`, ${fmtUsd(p.investmentUsd)} announced`); if (p.expectedOpening) bits.push(`, expected ${fmtPartialDateSafe(p.expectedOpening)}`); return `${bits.join(" ")}. Lifecycle stages, timeline, claims with scope and provenance.`; } /** A header figure may only stand for the project when its scope is a site scope (or unknown-but-assigned legacy). */ function siteScoped(scope: string | null | undefined): boolean { return scope === null || scope === undefined || isSiteScopeLabel(scope); } export async function generateMetadata({ params }: Props): Promise { const { slug } = await params; const r = await getProject(slug); if (!r.ok) return r.status === 404 ? NOT_FOUND_METADATA : { title: "Project" }; return pageMetadata({ title: r.data.name, description: describe(r.data), path: routes.project(r.data.slug) }); } function humanType(t: string): string { return EVENT_LABEL[t as EventType] ?? t.replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase()); } function toneFor(t: string): TimelineEntry["tone"] { switch (t) { case "construction_started": case "phase_announced": return "warn"; case "facility_opened": case "cloud_region_launched": return "ok"; case "project_announced": case "expansion_announced": case "investment_announced": case "power_agreement": return "accent"; case "closure": case "incident": case "project_cancelled": return "danger"; default: return "neutral"; } } function Figure({ label, children, note }: { label: ReactNode; children: ReactNode; note?: ReactNode }) { return (
{label}
{children}
{note &&
{note}
}
); } const PROJECT_CLASS_LABEL: Record = { NEW_BUILD: "New build", EXPANSION: "Expansion", CONSTRUCTION_START: "Construction start", PERMIT: "Permit", LAND_ACQUISITION: "Land acquisition", GRID_CONNECTION: "Grid connection", POWER_AGREEMENT: "Power agreement", FINANCING: "Financing", ACQUISITION: "Acquisition", PARTNERSHIP: "Partnership", CUSTOMER_AGREEMENT: "Customer agreement", }; export default async function ProjectPage({ params }: Props) { const { slug } = await params; const r = await getProject(slug); if (!r.ok && r.status === 404) notFound(); if (!r.ok) { return ( ); } const p = r.data; const base = routes.project(p.slug); const place = placeLine(p); const hasGeo = p.lat !== null && p.lng !== null; const precision = p.geoPrecision ?? (hasGeo ? "city" : "unknown"); const claims = p.claims ?? []; const history = p.history ?? []; const mwSite = siteScoped(p.capacityScope); const usdSite = siteScoped(p.investmentScope); const dq = p.dataQuality; const timeline: TimelineEntry[] = [...p.timeline] .sort((a, b) => partialDateSortKey(a.date.length > 10 ? a.date.slice(0, 10) : a.date) - partialDateSortKey(b.date.length > 10 ? b.date.slice(0, 10) : b.date)) .map((t) => ({ date: t.date, title: humanType(t.type), description: t.description, meta: t.sourceName ? {t.sourceName} : undefined, href: t.url, tone: toneFor(t.type) })); for (const s of p.statusHistory) timeline.push({ date: s.date, title: `${s.from ? `${STATUS_LABEL[s.from]} → ` : ""}${STATUS_LABEL[s.to]}`, href: s.url, tone: s.to === "under_construction" ? "warn" : s.to === "operational" ? "ok" : s.to === "cancelled" || s.to === "delayed" ? "danger" : "neutral" }); if (p.expectedOpening) timeline.push({ date: p.expectedOpening, title: Expected opening, description: p.phaseCount ? `phase 1 of ${fmtInt(p.phaseCount)} — expected, as published by the source` : "expected, as published by the source", tone: "neutral" }); timeline.sort((a, b) => partialDateSortKey(a.date.slice(0, 10)) - partialDateSortKey(b.date.slice(0, 10))); const sections = [ { id: "lifecycle", label: "Lifecycle" }, { id: "figures", label: "Figures & claims" }, { id: "timeline", label: "Timeline", count: timeline.length }, { id: "about", label: "About", count: p.description || p.sourceUrl || p.facility || p.campus ? 1 : 0 }, { id: "location", label: "Location" }, { id: "nearby", label: "Nearby", count: hasGeo ? 1 : 0 }, { id: "related", label: "Related events", count: p.relatedEvents.length + p.events.length }, { id: "provenance", label: "Source evidence", count: p.provenance.length }, { id: "quality", label: "Data quality" }, ]; const jsonLd = compactJsonLd({ "@context": "https://schema.org", "@type": "Project", name: p.name, description: p.description ?? undefined, url: absoluteUrl(base), sameAs: p.sourceUrl ?? undefined, foundingDate: p.announcedOn ?? undefined, parentOrganization: p.operator ? { "@type": "Organization", name: p.operator.name, url: absoluteUrl(routes.operator(p.operator.slug)) } : undefined, location: place !== "—" || hasGeo ? compactJsonLd({ "@type": "Place", name: place !== "—" ? place : undefined, address: p.countryIso2 ? { "@type": "PostalAddress", addressLocality: p.city ?? undefined, addressRegion: p.regionName ?? undefined, addressCountry: p.countryIso2 } : undefined, geo: hasGeo && precision !== "unknown" ? { "@type": "GeoCoordinates", latitude: p.lat, longitude: p.lng } : undefined }) : undefined, additionalProperty: [ { "@type": "PropertyValue", name: "status", value: STATUS_LABEL[p.status] }, p.plannedMw !== null && mwSite ? { "@type": "PropertyValue", name: "Planned capacity", value: p.plannedMw, unitText: "MW" } : null, { "@type": "PropertyValue", name: "geoPrecision", value: PRECISION_LABEL[precision] }, ].filter(Boolean), }); const mwSplit = splitMw(p.plannedMw); return (
Project {p.projectClass && PROJECT_CLASS_LABEL[p.projectClass] && ( <> · {PROJECT_CLASS_LABEL[p.projectClass]} )} {p.operator && ( <> · {p.operator.name} )}

{p.name}

{!p.operator && Operator not published} {p.developer && p.developer.id !== p.operator?.id && ( developed by{" "} {p.developer.name} )} {p.tenant && ( for{" "} {p.tenant.name} )} {(place !== "—" || p.metro) && ( {p.metro && ( {p.metro.name} market )} {p.metro && place !== "—" && ·} {place !== "—" && {place}} )}
{p.isAi && } {p.evidenceLevel && ( {p.evidenceLevel} evidence )} {p.phaseCount !== null && ( {fmtInt(p.phaseCount)} phases )}
{/* header figures */}
{p.plannedMw !== null && mwSite && mwSplit ? (
{mwSplit.value} {mwSplit.unit}
{p.capacityScope && }
) : (
—
{p.plannedMw !== null ? `${fmtMw(p.plannedMw)} is a ${p.capacityScope}-level figure — not this site's (see claims)` : "Not published by any source"}
)}
{p.investmentUsd !== null && usdSite ? (
{fmtUsd(p.investmentUsd)}
{p.investmentScope && } {p.investmentCurrency && p.investmentCurrency !== "USD" && p.investmentOriginal && {fmtInt(p.investmentOriginal)} {p.investmentCurrency}}
) : (
—
{p.investmentUsd !== null ? `${fmtUsd(p.investmentUsd)} is a ${p.investmentScope}-level figure — not this site's (see claims)` : "Not disclosed"}
)}
{p.expectedOpening ? fmtPartialDateSafe(p.expectedOpening) : "—"}
{p.announcedOn ? fmtDate(p.announcedOn) : "—"}

Planned MW and investment are published figures (≈ never imputed). A company-wide or portfolio figure is never shown as this project's own; it is listed under “Figures not assigned to this site”. Click a figure for its evidence.

Velocity
Capacity claims
Investment claims
{((p.plannedMw !== null && !mwSite) || (p.investmentUsd !== null && !usdSite)) && (

Figures not assigned to this site

scope is wider than the project — shown as context only
≈ {fmtMw(p.plannedMw)} : null }, { label: "Investment", value: p.investmentUsd !== null && !usdSite ? {fmtUsd(p.investmentUsd)} : null }, ]} />
)}
{timeline.length ? :

No dated milestone has been published for this project yet.

}
{p.description ?

{p.description}

:

No description published.

} {p.operator.name} : Not published, keepEmpty: true }, { label: "Developer", value: p.developer ? {p.developer.name} : Not published, keepEmpty: true }, { label: "Tenant", value: p.tenant ? {p.tenant.name} : null }, { label: "Facility", value: p.facility ? {p.facility.name} : null }, { label: "Campus", value: p.campus ? {p.campus.name} : null }, { label: "Site", value: p.acreage !== null ? {fmtInt(p.acreage)} acres : null }, { label: "Primary source", value: p.sourceUrl ? : null }, { label: "Last update", value: {fmtDate(p.lastUpdate)} }, ]} />
}>
{countryName(p.countryIso2)} : null }, { label: "Market", value: p.metro ? {p.metro.name} : null }, { label: "Coordinates", value: hasGeo ? {p.lat!.toFixed(precision === "exact" || precision === "parcel" ? 4 : 2)}, {p.lng!.toFixed(precision === "exact" || precision === "parcel" ? 4 : 2)} : Not published, keepEmpty: true }, { label: "Precision", value: , keepEmpty: true }, ]} /> {hasGeo ? :
No coordinates published — not drawn on the map.
}
{hasGeo ? :

Nearby infrastructure needs a published location for this project.

}
{p.sourceHistory.length > 0 && (
Source observation log {fmtInt(p.sourceHistory.length)}
)}

{CONFIDENCE_LABEL[p.confidence]} overall.{" "} How confidence is computed

); }